package com.pocket.insight.entity;

import com.alibaba.fastjson.JSONObject;
import com.pocket.insight.base.constant.UserConst;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 描述：
 *
 * @author shuize
 * @create 2024/11/28
 */
@Data
public class ME {

    public static String rowsWindow = "ROWS";
    public static String rangeWindow = "RANGE";

    public Kind          kind;
    public Object        op;
    public Type.TypeName type;
    public List<Object>  args;
    public String        uid;
    public Object        dataset;
    public String        direction;

    public String layer;
    public Object value;
    public Boolean remaining;
    public Boolean including;
    public Window window;
    public List<ME> filter;
    public List<ME> within;
    public Boolean orderByOut;
    public String baseUid;
    public NullsOrder nullsOrder;
    public Boolean datasetForceNative;


    public static List<ME> from(List<Object> object) {
        return (List<ME>) CollectionUtils.emptyIfNull(object).stream().map(ME::from).collect(Collectors.toList());
    }
    public static ME from(ME expr) {
        handleLegacyAttributes(expr);
        return expr;
    }
    public static ME from(Object object) {
        if (object instanceof ME) {
            return from((ME)object);
        }
        if (object instanceof String) {
            return create(Kind.field, object);
        }
        if (object instanceof JSONObject) {
            ME expr = ((JSONObject)object).toJavaObject(ME.class);
            if (expr.kind == Kind.field && CollectionUtils.isNotEmpty(expr.args)) {
                ME dataset = from(expr.args.get(0));
                if (dataset.kind == Kind.dataset) {
                    expr.dataset = dataset.op;
                    if (expr.dataset instanceof Number && !(expr.dataset instanceof Long)) {
                        expr.dataset = Long.valueOf(((Number)expr.dataset).longValue());
                    }
                }
                expr.args = null;
            }
            return expr;
        }
        if (object instanceof Map) {
            return from(new JSONObject((Map)object));
        }
        if (object instanceof List) {
            return create(Kind.array, object);
        }
        return create(Kind.constant, object);
    }
    public static void handleLegacyAttributes(ME expr) {
        if (expr == null) {
            return;
        }
        if (expr.kind == Kind.field || expr.kind == Kind.attr) {
            String name = (expr.op instanceof String) ? (String)expr.op : null;
            if (name == null) {
                return;
            }
            if (name.startsWith("$user.config.")) {
                expr.kind = Kind.attr;
                name = name.substring("$user.config.".length());
                if (UserConst.SYSTEM_DEFAULT_ATTRS.contains(name)) {
                    name = "sys." + name;
                }
            } else if (name.startsWith("$user.")) {
                expr.kind = Kind.attr;
                name = name.substring("$user.".length());
                if (UserConst.SYSTEM_DEFAULT_ATTRS.contains(name)) {
                    name = "sys." + name;
                }
            }
            expr.op = name;
        }
    }


    public static Long parseDatasetId(Object dataset) {
        if (dataset instanceof Number) {
            return Long.valueOf(((Number)dataset).longValue());
        }
        try {
            return Long.valueOf(Long.parseLong((String)dataset));
        } catch (Exception exception) {
            return null;
        }
    }


    /********************************* ME 构造器 *********************************/
    public static ME create(Kind kind, Object op) {
        ME expr = new ME();
        expr.kind = kind;
        expr.op = op;
        return expr;
    }

    public static ME create(Kind kind, Object op, List<Object> args) {
        ME expr = create(kind, op);
        expr.args = args;
        return expr;
    }

    public static ME create(Kind kind, Object op, List<Object> args, String uid) {
        ME expr = create(kind, op, args);
        expr.uid = uid;
        return expr;
    }

    public static Boolean isConstantStar(ME expr) {
        return expr != null && StringUtils.isBlank(expr.uid) && expr.kind == ME.Kind.constant && expr.op instanceof Star;
    }

    public ME setDataset(Object dataset) {
        this.dataset = dataset;
        return this;
    }

    public ME setUid(String uid) {
        this.uid = uid;
        return this;
    }

    public ME setType(Type.TypeName type) {
        this.type = type;
        return this;
    }

    public void addFilter(List<ME> filter) {
        if (CollectionUtils.isEmpty(filter)) {
            return;
        }
        if (this.filter == null) {
            this.filter = new ArrayList<>();
        }
        this.filter.addAll(filter);
    }

    public static class Window {
        public List<ME> order;
        public List<ME> partition;
        public Integer  lowerBound;
        public Integer  upperBound;
        public String   rowsRange = ME.rowsWindow;
        public String   lowerRange;
        public String upperRange;
        public Boolean hasFrameClause = Boolean.valueOf(false);
    }

    /**
     * 表达式类型
     */
    public enum Kind {
        field,
        function,
        reference,
        constant,
        dataset,
        setgroup,
        rangegroup,
        formula,
        array,
        set,
        casewhen,
        attr,
        param,
        apiparam,
        measure;
    }

    public enum Star {
        STAR;
    }

    public enum NullsOrder {
        LAST,
        FIRST;
    }
}
